home *** CD-ROM | disk | FTP | other *** search
- #Titel Programmieren / Verzeichnisse mit PCQ lesen
- #Logo gadget25:Pinsel/AG.Prog
- #Font Losse 16
- #C31
- Verzeichnisse mit PCQ lesen
- #Font topaz 8
- #C10
- ein neuer Teil des sporadisch fortgeführten PCQ-Kurses
-
- #C21
- Nachdem im Rahmen dieses Kurses bereits die grundlegenden Elemente von
- "Intuition" vorgestellt, die Funktionsweise doppelt verketteter Listen
- erklärt und auch eine Laderoutine für IFF-Dateien entwickelt wurde, soll
- diesmal ein völlig anderes Thema angesprochen werden - es geht um einige
- Funktionen der dos.library.
-
- Obwohl wir im Zeitalter von Kickstart 3.1, der asl.library und MUI leben,
- dem Programmierer mithin bereits zahlreiche Funktionen zur Verfügung
- gestellt werden, wenn es um die Handhabung von Verzeichnisinhalten geht (wie
- z.B. den asl-Filerequester), können nach wie vor Situationen eintreten,
- in denen einem nichts anderes übrig bleibt, als sich selbst die
- entsprechenden Informationen zu besorgen. Dies soll im folgenden erläutert
- und anhand eines eigenen (primitiven) "Dir"-Ersatzes dargestellt werden.
- Doch fangen wir von ganz vorne an.
-
- Ein Datenträger - sei es Festplatte, CD-ROM oder Diskette - kann
- bekanntermaßen nicht nur mit einzelnen Dateien gefüllt, sondern der
- besseren Strukturierung wegen auch in einzelne Verzeichnisse und
- hierarchisch untergeordnete Unterverzeichnisse unterteilt werden. Das kennt
- man jedenfalls von der Workbench-Ebene durch die dort verwendeten
- Schubladen-Symbole, zu denen auch tatsächlich entsprechende Verzeichnisse
- auf dem Datenträger gehören (im Gegensatz zur weitaus umständlicheren
- Organisationsstruktur etwa von OS/2 Warp oder Windows 3.xx). Um die
- wenig spektakuläre Materie doch nochmals ein weiteres Stückchen zu
- verdeutlichen - skizziert man den Aufbau eines Datenträgers, ergibt sich
- folgende Struktur:
- #Seitenende
- #C10
- #Y+30
- Datenträger-"Root"-Verzeichnis
- ____________________________|___________________________________
- | | | | | |
- Datei 1 Datei 2 [...] Datei n Verzeichnis 1 Verzeichnis 2 [...] Verzeichnis N
- | | |
- | [entsprechend dem Verlauf bei Verz. 1]
- |
- ____________________________|___________________________________
- | | | | | |
- Datei a Datei b [...] Datei x Verzeichnis a Verzeichnis b [...] Verzeichnis X
- | | |
- [...] [...] [...]
- #Seitenende
- #C21
- Bei kleineren Datenträgern wie Disketten ergibt sich aus Platzgründen eine
- Beschränkung der Komplexität, so daß man sich problemlos merken kann, wo
- man was auf ihm findet. Anders sieht das mit größeren Datenträgern aus und
- spätestens ab der 1 GByte-Festplatte muß man schon über ein außergewöhnliches
- Gedächtnis verfügen, um sich den Inhalt sämtlicher Verzeichnisse und
- Unterverzeichnisse merken zu können. Da diese Lösung auch äußerst
- unpraktisch ist, gibt es seit jeher zahlreichen Hilfen zur Bewältigung
- der Dateienflut - die bekannteste dürfte dabei natürlich die Workbench sein,
- mit deren Hilfe es möglich ist, sich intuitiv durch die Verästelungen der
- "Schubladen"-Hierarchie zu hangeln. Als zusätzliche Hilfe dienen auch all
- die Dateimanager, deren bekanntester Vertreter sicherlich "DirOpus" sein
- dürfte. Doch all das sind Aufsätze. Auf der untersten Ebene, dem CLI, bzw.
- der Shell, gilt es mit äußerst bescheidenen Mitteln der Dateienflut Herr
- zu werden. Wichtigstes Hilfsmittel ist der "dir"-Befehl, der dem Anwender
- den kompletten Inhalt des aktuellen Verzeichnisses in Textform ausgibt.
- Da er über einige Parameter verfügt (etwa Joker wie "#?" oder das Flag
- "-a", mit dem rekursiv zusätzlich sämtliche Unterverzeichnisse und deren
- Inhalt ausgegeben werden), läßt sich bereits sehr gut mit ihm arbeiten.
- Findige Programmierer haben den ursprünglichen Befehl natürlich noch weiter
- ausgebaut und verbessert, so daß man im FD-Pool weitaus mächtigere
- "dir"-Clones finden kann. Doch all das ist nur etwas für den Anwender. Was
- aber macht der Programmierer, dessen Programm den Inhalt eines
- Verzeichnisses herausfinden soll ?
-
- Ansatzpunkt ist die Funktion
-
- #C10
- Function ExNext(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
- #C21
-
- der "dos.library". Ihre beiden Parameter sind
-
- #C10
- lock:
- #C21
- #Y-10
- Lock (bzw. ein BCPL-Zeiger auf diesen Lock) eines Verzeichnisses,
- dessen Inhalt "gescannt" werden soll.
-
- #C10
- info:
- #C21
- #Y-10
- Zeiger auf einen FileInfoBlock, in dem die Informationen zum jeweils
- gelesenen Verzeichniseintrag zu finden sind.
-
- Das hilft uns noch nicht viel weiter, denn es stellen sich zwei Fragen:
-
- 1.) Wie bekomme ich so einen "Lock" auf ein Verzeichnis ? Und was ist das
- überhaupt ?
- 2.) Was steht alles in dem FileInfoBlock und wie handhabe ich ihn ?
-
- #C10
- zu 1.):
- #C21
- #Y-10
- Grundsätzlich ist ein "Lock" das Ergebnis der konsequenten
- Multitasking-Ausrichtung des Amiga-Betriebssystems. Man stelle sich folgende
- Situation vor: ein Programm durchforstet ein Verzeichnis namens "texte".
- Da sich darin sehr viele Dateien befinden, dauert das Spielchen eine
- ganze Zeit. Währenddessen ändert ein heimtückischer Anwender in einem
- parallel laufenden CLI-Prozess den Verzeichnisnamen "texte" in "reingelegt".
- Sucht das Programm nun einfach die nächste Datei im Verzeichnis "texte",
- dann muß es - da dieses Verzeichnis ja so nicht mehr existiert - scheitern,
- wenn es dieses Verzeichnis nur über den Namen identifizieren würde.
- Diese Gefahr wird mit den Locks umgangen. Denn so wird ein Verzeichnis
- (und auch eine Datei) nicht mehr über ihren Namen zugeordnet, es entsteht
- vielmehr eine Art "Zeiger", der nun jedoch nicht auf eine Datenstruktur
- im Speicher, sondern auf eine Datei auf einem Datenträger zeigt. Ändert
- sich der Name der Datei hat das keine Auswirkungen auf die korrekte
- Bezeichnung der Datei durch den "Zeiger", den Lock. Einen solchen
- "Zeiger" auf ein Verzeichnis oder ein File erhält man ganz problemlos
- als Rückgabewert der Funktion
-
- #C10
- Function Lock(name : String; accessmode : Integer) : FileLock;
-
- #C10
- "name" "accessmode"
- #C21
- #Y-10
- Dabei steht in der Name der Datei/des Verzeichnisses. Und
- enthält einen der beiden Werte
-
- #C10
- ACCESS_READ = -2;
- #C21
- #Y-10 oder
- ACCESS_WRITE = -1;
-
- #C21
- Synonym für kann auch verwendet werden - in
- #C10
- #Y-10
- "ACCESS_READ" "SHARED_LOCK"
- #C21
- diesem Zugriffsmode können auch andere Programme einen entsprechenden
- Lock auf die Datei/das Verzeichnis erhalten. Das geht nicht bei
- #C10
- "ACCESS_WRITE" "EXCLUSIVE_LOCK"
- #C21
- #Y-10
- (entspricht ). Hier hat man den Lock auf die
- entsprechende Datei/das entsprechende Verzeichnis für sich alleine - was
- etwa bei Schreibzugriffen natürlich sinnvoll ist: wird eine Datei z.B. erst
- erstellt, so würden Programme, die zeitgleich dazu die Größe der Datei
- zu ermitteln versuchen, lediglich kaum sinnvolle Werte erhalten. Solche
- Verwirrungen werden mit einem WRITE-Lock ausgeschlossen.
-
- Der Rückgabewert der Funktion ist nun der langgesuchte Lock. Bevor wir aber
- in der Theorie weiterschreiten, soll noch darauf hingewiesen werden, daß
- man niemals vergessen sollte, ein Lock (insbesondere ein WRITE-Lock) mit
-
- #C10
- Procedure UnLock(lock : FileLock);
- #C21
-
- wieder freizugeben. Nicht nur, daß man mit falschen Parametern bei einem
- Aufruf der "Lock"-Funktionen erhebliche Konfusion im AmigaDOS stiften kann -
- ein nicht geschlossener Lock kann auch sonst unangenehm werden. So lassen
- sich beispielsweise Dateien, auf die noch ein offener Lock besteht, nicht
- löschen. Man sollte mithin immer dafür sorgen, daß ein selbstgeschriebenes
- Programm vor dem Verlassen sauber alle vorher erstellten Locks wieder
- freigibt.
-
- #C10
- zu 2.)
- #C21
- #Y-10
- Ein Lock auf das zu durchsuchende Verzeichnis hilft uns an sich aber
- ja noch nicht weiter. Nun kommt der FileInfoBlock zum Zug. Es handelt sich
- bei ihm um eine Struktur der "dos.library", die wie folgt aussieht:
-
- #C10
- FileInfoBlock = record
- fib_DiskKey : Integer;
- fib_DirEntryType : Integer;
- #Y-10
- #C21 Dieser Wert zeigt an, ob es sich
- bei dem "File" um ein Verzeichnis
- (dann ist er größer Null) oder um
- eine normale Datei handelt (dann
- gilt fib_DirEntryType<0).
- #C10
- fib_FileName : Array [0..107] of Char;
- #C21
- Hier erhält man den - auch
- hinsichtlich etwaiger Groß- und
- Kleinschreibung - korrekten
- und vollständigen Dateinamen.
- Damit man mit dem umständlichen
- Char-Feld arbeiten kann, empfiehlt
- es sich, einen Zeiger auf den
- Feldeintrag 0 einer String-Variablen
- zuzordnen. Das funktioniert
- problemlos, da fib_FileName mit
- einem Nullbyte endet, was ja auch
- für PCQ das Ende eines Strings
- symbolisiert.
-
- #C10
- fib_Protection : Integer;
- #C21
- #Y-10
- Hinter diesem Wert verbergen sich
- die Protection-Flags (zu sehen etwa
- über die "Information" im WB-Menü
- oder mittels des "List"-Befehls im
- CLI). Die Flags sind dabei als
- "BitMask" angeordnet, d.h. ein
- gesetztes Bit an einer bestimmten
- Position bedeutet ein gesetztes
- Flag. Die vier wichtigsten
- Protection-Flags sind "Read",
- "Write", "Execute" und "Delete".
- Diese vier verbergen sich in
- genau dieser Reihenfolge in den
- untersten vier Bits des Integer-
- Wertes. D.h.: ist Bit 0 gesetzt,
- dann ist die Datei löschgeschützt,
- ist es nicht gesetzt, kann sie
- gelöscht werden. Anderes Beispiel:
- Ist Bit 3 gesetzt, kann die Datei
- nicht gelesen werden. Die
- einzelnen Protection-Bits sind auch
- in "Libraries/Dos.I" definiert.
- Für die vier hier etwas näher
- vorgestellten, heissen die
- Flag-Werte
-
- #C10
- FIBF_READ = 8;
- FIBF_WRITE = 4;
- FIBF_EXECUTE = 2;
- FIBF_DELETE = 1;
- #C21
-
- Im Programm kann man die einzelnen
- Protection-Bits dann so abfragen:
-
- #C10
- BitGesetzt:=
- ((IntWert AND FIB-Wert)=FIB-Wert)
- fib_EntryType : Integer;
- fib_Size : Integer;
- #C21
- #Y-10
- Dieser Wert enthält die Größe der
- Datei in Bytes.
-
- #C10
- fib_NumBlocks : Integer;
- #C21
- #Y-10
- Diesem Wert kann man entnehmen,
- wieviele Blöcke die Datei belegt.
-
- #C10
- fib_Date : DateStampRec;
- #C21
- #Y-10
- Hier steht das Datum, an dem die
- Datei das letzte Mal geändert wurde.
- Allerdings ist die Entschlüsselung
- nicht so ganz einfach, da ein
- DateStamp nur die Differenz zum
- 1.1.1978 in Tagen enthält...
-
- #C10
- fib_Comment : Array [0..79] of Char;
- #C21
- Hier steht ein etwaiger Kommentar
- zu der Datei. Zur programmtechnischen
- Handhabung gilt das schon beim
- Filenamen gesagte.
- #C10
- fib_Reserved : Array [0..35] of Char;
- end;
- #C21
-
- Diese Informationsflut ist ja schon recht verlockend. Um sie jedoch für
- uns nutzen zu können, müssen wir zunächst entsprechenden Speicherplatz
- (mit "AllocMem") belegen und dann die Funktion
-
- #C10
- Function Examine(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
- #C21
-
- mit dem vorher erstellten Lock auf das zu durchforstende Verzeichnis und
- dem Zeiger auf den vorher ja allozierten Speicher für den FileInfoBlock (FIB)
- aufrufen. Dies ist notwendig, damit der FIB die notwendigen Ausgangsdaten
- enthält. Um nun die einzelnen Verzeichnisinhalte einzulesen, brauchen wir
- nichts weiter tun, als so lange die Funktion
-
- #C10
- Function ExNext(lock : FileLock; info : FileInfoBlockPtr) : Boolean;
- #C21
-
- mit genau denselben Parametern wie zuvor bei der einmaligen Verwendung von
- "Examine" aufzurufen, bis der Rückgabewert FALSE ist. Zwischen zwei
- Aufrufen kann dann der jeweils sich anpassende Inhalt der FIB-Struktur
- ausgewertet, d.h. insbesondere Informationen zum Verzeichnisinhalt
- ausgegeben oder weiteren Unterverzeichnissen gefolgt werden. Letzteres
- ist natürlich nicht ganz so einfach, programmtechnisch allerdings recht
- elegant mit einer einfacher Rekursion (sprich: dem Aufruf der ReadDir-
- Routine durch sich selbst mit neuen Parametern) gelöst werden. Das geht
- zwar problemlos, jedoch sollte man sich zwei Dinge vor Augen halten:
-
- 1. Bei Rekursionen wird in PCQ der Stack belastet, d.h. bei sehr stark
- verschachtelten (die Betonung liegt auf _sehr_) Verzeichnisstrukturen
- kann es hier zu Problemen kommen.
-
- 2. Bearbeitet man nicht das aktuelle, sondern ein vom Benutzer zu
- bestimmendes Verzeichnis, so muß man bei rekursiven Routinenaufrufen
- den Verzeichnisnamen "manuell" um den Namen des Unterverzeichnisses
- ergänzen. Damit macht man sich aber die nicht auf eine über den Namen
- erfolgende Zuordnung beschränkte "Lock"-Technik über ein
- Hintertürchen wieder kaputt.
-
- Beide Probleme können selbstverständlich gelöst werden. Im Rahmen dieses
- Kurzüberblickes soll es jedoch mit dem Hinweis auf ihre Existenz ein
- Bewenden haben.
-
- Liefert "ExNext" schließlich FALSE, gibt man den für den FIB benutzten
- Speicherbereich wieder frei, ruft "UnLock" auf und ist fertig. Wie das
- ganze in der Praxis aussieht, soll mit dem nun anschließenden Programm
- demonstriert werden.
- #Seitenende
- #C10
- PROGRAM ReadDir;
-
- {
- Demonstrationsprogramm für das Kapitel "Einladen eines Verzeichnisinhaltes
- des AmigaGadget-PCQ-Kurses
-
- written by Andreas Neumann 08.06.1996
-
- das Programm wird über CLI mit dem Namen des anzuzeigenden Verzeichnisses
- aufgerufen und gibt sämtliche Dateien dieses Directories, sowie die
- Dateien in eventuellen Unterverzeichnissen, aus
-
- Achtung: nicht bei sehr stark verschachtelten Verzeichnissen verwenden,
- da ansonsten der Stack "überlaufen" und das System abstürzen
- könnte !
- }
- #Seitenende
- {$I "Include:libraries/dosextens.i" }
- {$I "Include:utils/Parameters.i" }
- {$I "Include:utils/Stringlib.i" }
- {$I "Include:exec/memory.i" }
-
- VAR dirname : String;
-
- PROCEDURE ZeigeVerzeichnisAn (einruecken : BYTE; verzeichnis : STRING);
-
- VAR
- zvLock : FileLock;
- zvInfo : FileInfoBlockPtr;
- zvi : INTEGER;
- zvStr2,
- zvStr : String;
-
- BEGIN
- zvInfo:=AllocMem (SIZEOF(FileInfoBlock),MEMF_CLEAR+MEMF_PUBLIC);
- IF zvInfo<>NIL THEN
- BEGIN
- zvLock:=Lock (verzeichnis,ACCESS_READ);
- IF zvLock<>NIL THEN
- BEGIN
- IF Examine (zvLock,zvInfo)=FALSE THEN
- BEGIN
- UnLock (zvLock);
- zvLock:=NIL;
- END
- ELSE
- BEGIN
- WHILE (ExNext (zvLock , zvInfo)) DO
- BEGIN
- zvi:=einruecken;
- WHILE zvi>0 DO
- BEGIN
- WRITE (' ');
- Dec(zvi);
- END;
- zvStr:=Adr(zvInfo^.fib_FileName);
- IF zvInfo^.fib_DirEntryType>0 THEN
- BEGIN
- WRITELN (zvStr,":");
- zvStr2:=AllocString(255);
- IF zvStr2<>NIL THEN
- BEGIN
- StrCpy (zvStr2,verzeichnis);
- IF (zvStr2[StrLen(zvStr2)-1]<>':') AND
- (zvStr2[StrLen(zvStr2)-1]<>'/') THEN
- IF (StrLen(verzeichnis)>0) THEN
- StrCat (zvStr2,"/");
- StrCat (zvStr2,zvStr);
- ZeigeVerzeichnisAn (einruecken+4,zvStr2);
- FreeString (zvstr2);
- END;
- END
- ELSE
- WRITELN (zvStr);
- END;
- UnLock (zvLock);
- zvLock:=NIL;
- END;
- END;
- FreeMem (zvInfo,SIZEOF(FileInfoBlock));
- END;
- END;
-
- BEGIN
- dirname:=AllocString (255);
- IF dirname<>NIL THEN
- BEGIN
- GetParam (1,dirname);
- ZeigeVerzeichnisAn (0,dirname);
- FreeString (dirname);
- END;
- END.
-
- #C21
- Man sieht: es ist wirklich recht problemlos zu realisieren. Das Demolisting
- ist dabei bewußt kurz gehalten - Erweiterungen, etwa die Anzeige von
- filespezifischen Informationen oder die Konfiguration über Flags oder
- die Sortierung der gelesenen Verzeichnisinhalte, dürften programmtechnisch
- kein ernsthaftes Problem sein und empfehlen sich dem interessierten
- Lernenden zur Übung...
-
- Wenn es Fragen gibt, Kritik, Anregungen oder was auch immer, dann
- ist das "AmigaGadget" der richtige Ort, um sie loszuwerden. Für heute
- soll es das gewesen sein. Viel Spaß beim Herumprobieren mit den neu
- kennengelernten Routinen wünscht
-
- #Pinsel gadget25:pinsel/an rechts
-